#!/usr/bin/python3
import fnmatch
import os
import subprocess
import sys
import threading
import time
from collections import Counter
import psutil
import yaml
import argparse
import logging
import json
from kubernetes import client, config, watch

"""
初始化参数
"""
parser = argparse.ArgumentParser()
parser.add_argument("--configPath", type=str, default="./config.yml")
parser.add_argument('--namespace', type=str, default='default')
parser.add_argument("--kubeconfig", type=str, default='./kubeconfig/config')
parser.add_argument('--svcName', type=str, default=None)
parser.add_argument('--maxme', type=str, default='1024')
parser.add_argument('--minme', type=str, default='512')
parser.add_argument('--nodePort', type=str, default=None)
parser.add_argument("--headless", type=bool, default=False)
parser.add_argument('--label', type=str)
parser.add_argument('--jar', type=str, default='BungeeCord.jar')
args = parser.parse_args()
config.load_kube_config(args.kubeconfig)
v1 = client.CoreV1Api()

# 创建logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# 创建FileHandler
file_handler = logging.FileHandler('server_list_monitor.log')
file_handler.setLevel(logging.INFO)

# 创建StreamHandler
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)

# 创建Formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

# 设置Formatter
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)

# 添加handler到logger
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

# 初始化BC参数
SERVICE = 'BungeeCord.jar'
SCREENNAME = 'bc'
OPTIONS = 'nogui'
HISTORY = 1024


def get_pid(name):
    """
     作用：根据进程名获取进程pid
    """
    pids = psutil.process_iter()
    for pid in pids:
        if pid.name() == name:
            return pid.pid


def bc_start():
    """
     作用：启动BC
    """
    os.system("screen -wipe")
    os.system("pkill screen")
    os.system("pkill java")
    logger.info(f"启动{SERVICE}...")
    os.system(
        f"screen -h {HISTORY} -dmS {SCREENNAME} java -Xmx{args.maxme}M -Xms{args.minme}M -jar {args.jar} nogui")  # 启动 BC
    screen_pid = get_pid("screen")
    bc_pid = get_pid("java")  # 查询PID
    bc_p = psutil.Process(bc_pid)  # 获取进程信息

    if bc_pid and screen_pid is None:
        logger.error("启动失败！！")
    else:
        logger.info("启动成功！！")
        logger.info("进程名称：%s，进程创建时间：%s,进程终端：%s" % (bc_p.name(), bc_p.create_time(), bc_p.terminal()))
        logger.info("bc准备中(10s)...")
        logger.info("bc准备完毕！！")


def bc_stop():
    """
     作用：关闭BC
    """
    os.system("screen -wipe")
    os.system(f"screen -p 0 -S {SCREENNAME} -X eval 'stuff \"end\"\015'")
    screen_pid = get_pid("screen")  # 查询PID
    bc_pid = get_pid("java")
    if screen_pid and bc_pid is None:
        logger.error(f"{SERVICE} 没有在运行！！")
    else:
        logger.info(f"关闭{SERVICE}...")
        os.system(f"screen -S {SCREENNAME} -X quit")
        os.kill(bc_pid, 9)
        logger.info(f"{SERVICE} 已关闭！！")


def bc_reload():
    """
     作用：重载BC后端服务器列表
    """
    java_pid = get_pid("java")  # 查询PID
    if java_pid:
        logger.info(f"{SERVICE} 正在运行....")
        # 执行bc命令
        cmds = [f"screen -p 0 -S {SCREENNAME} -X eval 'stuff \"alert 检测到服务器列表变动，将重新加载...\"\015'",
                f"screen -p 0 -S {SCREENNAME} -X eval 'stuff \"greload\"\015'"]
        for cmd in cmds:
            os.system(cmd)
        bc_stop()
        time.sleep(3)
        bc_start()
    else:
        logger.error("bc未运行!!请检查bc配置是否有误!")


def get_pods_name(namespace: str, label: str = None, name_list: bool = False):
    pods = []
    pod_list = v1.list_namespaced_pod(namespace, label_selector=label)
    for pod in pod_list.items:
        if name_list:
            pods.append(pod.metadata.name)
        else:
            pods.append({'name': pod.metadata.name})
    return pods


def coop_with(config_path: str, svc_name, headless: bool):
    """
    作用：修改bc config.yml文件
    """
    with open(rf'{config_path}', "r", encoding='utf-8') as file:
        data = yaml.load(file, Loader=yaml.FullLoader)
        num = 0
        data["ip_forward"] = True
        data["forge_support"] = True
        data["servers"] = {}
        data['listeners'][0]['priorities'] = []
        if headless:
            for i in get_pods_name(args.namespace, args.label):
                datas = {
                    f'lobby{num}': {'address': f'{i["name"]}.{svc_name}:25565',
                                    'motd': f'MC Cluster Node {num}',
                                    'restricted': False}
                }
                data['listeners'][0]['priorities'].append(f'lobby{num}')
                num += 1
                data["servers"].update(datas)

        else:
            for i in get_node_ips():
                datas = {
                    f'lobby{num}': {'address': f'{i}:{args.nodePort}',
                                    'motd': f'MC Cluster Node {num}',
                                    'restricted': False}
                }
                data['listeners'][0]['priorities'].append(f'lobby{num}')
                num += 1
                data["servers"].update(datas)


    with open("config.yml", "w", encoding='utf-8') as file:
        yaml.dump(data, file)
    logger.info("修改结果：")
    logger.info(json.dumps(data["servers"], sort_keys=True, indent=2))


def get_bc_backend_address_list(headless: bool):
    """
    作用：获取config.yml文件内所有服务器地址
    """
    address_list = []
    with open(rf'{args.configPath}', "r", encoding='utf-8') as file:
        data = yaml.load(file, Loader=yaml.FullLoader)
        if headless:
            delimiter = "."
        else:
            delimiter = ":"
        for i in data["servers"]:
            address_list.append(data["servers"][i]["address"].split(delimiter)[0])
    return address_list


def get_node_ips():
    # 获取所有节点的信息
    nodes = v1.list_node()
    node_ips = []
    for node in nodes.items:
        for status in node.status.addresses:
            if status.type == "InternalIP":
                node_ips.append(status.address)
                break  # 找到第一个InternalIP后就跳出循环

    return node_ips


def find_proxy_logs(directory='./'):
    proxy_logs = []
    while True:
        for root, dirnames, filenames in os.walk(directory):
            for filename in fnmatch.filter(filenames, 'proxy.log*'):
                if not filename.endswith('.lck'):
                    proxy_logs.append(os.path.join(root, filename))
        if proxy_logs:
            break  # 如果找到了文件，退出循环
        else:
            logger.info("未找到bc日志，等待bc启动...")
            time.sleep(5)  # 等待 5 秒后再次尝试

    return proxy_logs


def bc_log_print():
    """使用 `tail -f` 实时打印日志文件"""
    process = subprocess.Popen(
        ['tail', '-F', find_proxy_logs()[0]],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    try:
        for line in iter(process.stdout.readline, b''):
            print(line.decode().strip())
    finally:
        process.terminate()
        process.wait()


def server_list_monitor_by_watch():
    apps_v1 = client.AppsV1Api()
    w = watch.Watch()
    for event in w.stream(apps_v1.list_namespaced_stateful_set, namespace=args.namespace, label_selector=args.label):
        if event['type'] in ['ADDED', 'DELETED', 'MODIFIED']:
            if Counter(get_bc_backend_address_list(headless=True)) == Counter(
                    get_pods_name(args.namespace, args.label, name_list=True)):
                logger.info(
                    f"服务器列表已同步, 当前本地服务器地址列表:{get_bc_backend_address_list(args.headless)}, "
                    f"集群服务器地址列表:{get_pods_name(args.namespace, args.label, name_list=True)}")
            else:
                logger.warning(
                    f"服务器列表未同步，当前本地服务器地址列表:{get_bc_backend_address_list(args.headless)}, "
                    f"集群服务器地址列表:{get_pods_name(args.namespace, args.label, name_list=True)}")
                logger.info("正在更新服务器列表...")
                time.sleep(3)
                coop_with(args.configPath, args.svcName, args.headless)
                time.sleep(3)
                bc_reload()
                if Counter(get_bc_backend_address_list(headless=True)) == Counter(
                        get_pods_name(args.namespace, args.label, name_list=True)):
                    logger.info('服务器列表更新成功！')
                    logger.info(
                        f"当前本地服务器地址列表:{get_bc_backend_address_list(args.headless)}, "
                        f"集群服务器地址列表:{get_pods_name(args.namespace, args.label, name_list=True)}")
                else:
                    logger.error("更新服务器列表失败！！")


def run_in_background(function):
    thread = threading.Thread(target=function)
    thread.daemon = True
    thread.start()


if __name__ == '__main__':
    """
    启动BC
    """
    if args.maxme == '1024' and args.minme == '512':
        """
         作用：检测是否设置内存大小并启动BC
        """
        logger.warning("未设置内存大小变量,将使默认值(1024M)")

    coop_with(args.configPath, args.svcName, args.headless)
    bc_start()
    run_in_background(bc_log_print)
    run_in_background(server_list_monitor_by_watch)
    # 主线程无限循环等待，确保主线程不退出
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        sys.exit(0)
